﻿using System;
using System.Threading;

namespace Columbus.Tests
{
    public sealed class AsyncResult : IAsyncResult, IDisposable
    {
        private readonly AsyncCallback callback;
        private bool completed;
        private bool completedSynchronously;
        private readonly object asyncState;
        private ManualResetEvent waitHandle;
        private object result;
        private Exception error;
        private readonly object syncRoot = new object();

        public AsyncResult(AsyncCallback cb, object state)
            : this(cb, state, false)
        { }

        private AsyncResult(AsyncCallback cb, object state, bool completed)
        {
            callback = cb;
            asyncState = state;
            this.completed = completed;
            completedSynchronously = completed;
        }

        public object AsyncState
        {
            get { return asyncState; }
        }

        public WaitHandle AsyncWaitHandle
        {
            get
            {
                lock (syncRoot)
                {
                    return waitHandle ?? (waitHandle = new ManualResetEvent(false));
                }
            }
        }

        public bool CompletedSynchronously
        {
            get
            {
                lock (syncRoot)
                {
                    return completedSynchronously;
                }
            }
        }

        public bool IsCompleted
        {
            get
            {
                lock (syncRoot)
                {
                    return completed;
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!disposing) return;
            lock (syncRoot)
            {
                if (waitHandle != null)
                {
                    ((IDisposable)waitHandle).Dispose();
                }
            }
        }

        public Exception Exception
        {
            get
            {
                lock (syncRoot)
                {
                    return error;
                }
            }
        }

        public object Result
        {
            get
            {
                lock (syncRoot)
                {
                    return result;
                }
            }
        }
        
        // ReSharper disable ParameterHidesMember
        public void Complete(object result, bool completedSynchronously)
        {
            lock (syncRoot)
            {
                completed = true;
                this.completedSynchronously = completedSynchronously;
                this.result = result;
            }

            SignalCompletion();
        }

        public void HandleException(Exception e, bool completedSynchronously)
        {
            lock (syncRoot)
            {
                completed = true;
                this.completedSynchronously = completedSynchronously;
                error = e;
            }

            SignalCompletion();
        }
        // ReSharper restore ParameterHidesMember

        private void SignalCompletion()
        {
            //Only need to signal WaitHandle if it has been used
            if (AsyncWaitHandle != null)
                ((ManualResetEvent)AsyncWaitHandle).Set();
            if (callback != null)
            {
                callback(this);
            }
        }
    }
}